﻿/*******************************************************************************************
 * Copyright (c) 2006-2009 Alex Robson, Rob Simmons, Craig Israel, and Rachel Twyford
 *
 * Use of this software in any form requires that you have both 
 * read and agreed to the following terms:
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the "Software"), 
 * to deal in the Software without restriction, including without limitation 
 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions: 
 *
 * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright notice, 
 * this list of conditions and the following disclaimer in the 
 * documentation and/or other materials provided with the distribution.
 * Neither the name of Nvigorate nor the names of its contributors may 
 * be used to endorse or promote products derived from this software without 
 * specific prior written permission. 
 *
 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
 * DISCLAIMED. IN NO EVENT SHALL ANY CONTRIBUTOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * ****************************************************************************************/

using System;
using System.Collections.Generic;
using System.Linq;
using Nvigorate.Data.Query;

namespace Nvigorate.Relational.Repository
{
    public class LoadInstruction<T> : ILoadInstruction
    {
        #region Fields

        private List<ILoadInstruction> _instructions = new List<ILoadInstruction>();
        private bool _completeLoad = true;
        private SearchCriteria _criteria = new SearchCriteria();
        private bool _eagerLoad = false;
        private Dictionary<string, Table> _joins = new Dictionary<string, Table>();
        private bool _limitByChildren = false;
        private int _recursionLevels = 0;
        private int _pageIndex = 0;
        private int _pageSize = 0;
        private int _totalRecords = 0;
        private List<string> _properties = new List<string>();
        private Order _orderClause = new Order();
        private Type _loadType;

        #endregion
        
        #region Properties

        public Dictionary<string, Table> AdditionalJoins
        {
            get { return _joins; }
            set { _joins = value; }
        }

        public SearchCriteria Criteria
        {
            get { return _criteria; }
            set { _criteria = value; }
        }

        public bool CompleteLoad
        {
            get { return _completeLoad; }
            set { _completeLoad = value; }
        }

        public List<ILoadInstruction> Instructions
        {
            get { return _instructions; }
            set { _instructions = value; }
        }

        public bool LimitByChildren
        {
            get { return _limitByChildren; }
            set { _limitByChildren = value; }
        }

        public bool RecursiveLoad
        {
            get { return _eagerLoad; }
            set { _eagerLoad = value; }
        }

        public int RecursionLevels
        {
            get { return _recursionLevels; }
            set { _recursionLevels = value; }
        }

        public int PageIndex
        {
            get { return _pageIndex; }
            set { _pageIndex = value; }
        }

        public int PageSize
        {
            get { return _pageSize; }
            set { _pageSize = value; }
        }

        public List<string> Properties
        {
            get { return _properties; }
            set { _properties = value; }
        }

        public Order OrderClause
        {
            get { return _orderClause; }
            set { _orderClause = value; }
        }

        public Type LoadType
        {
            get { return _loadType; }
            set { _loadType = value; }
        }

        #endregion

        public LoadInstruction<T> ChildInstructions(params ILoadInstruction[] children)
        {
            _instructions = new List<ILoadInstruction>(children);
            return this;
        }

        public LoadInstruction<T> ChildInstruction<C>(Func<LoadInstruction<C>, ILoadInstruction> childInstruction)
        {
            if(_instructions == null)
                _instructions = new List<ILoadInstruction>();

            _instructions.Add(childInstruction(LoadInstruction<C>.New()));
            return this;
        }

        public LoadInstruction<T> ConstrainByChildren(bool constrain)
        {
            _limitByChildren = constrain;
            return this;
        }

        public LoadInstruction<T> EagerLoad()
        {
            _eagerLoad = true;
            return this;
        }

        public LoadInstruction<T> JoinTable(string table, Table joinSpecification)
        {
            _joins.Add(table, joinSpecification);
            return this;
        }

        public LoadInstruction<T> LimitRecursion(int levels)
        {
            _recursionLevels = levels;
            return this;
        }

        public LoadInstruction<T> LoadAllProperties()
        {
            _completeLoad = true;
            return this;
        }

        public LoadInstruction<T> LoadOnly(params string[] properties)
        {
            _completeLoad = false;
            _properties = new List<string>(properties);
            return this;
        }

        public LoadInstruction<T> OrderBy(Order order)
        {
            _orderClause = order;
            return this;
        }

        public LoadInstruction<T> Page(int pageIndex)
        {
            _pageIndex = pageIndex;
            return this;
        }

        public LoadInstruction<T> PageBy(int recordsPerPage)
        {
            _pageSize = recordsPerPage;
            return this;
        }

        public LoadInstruction<T> StoreRecordTotal(ref int totalRecord)
        {

            return this;
        }

        public LoadInstruction<T> Where(SearchCriteria criteria)
        {
            _criteria = criteria;
            return this;
        }

        public LoadInstruction<T> Where(Func<DomainCriterion<T>, SearchCriteria> criteria)
        {
            _criteria = criteria(new DomainCriterion<T>());
            return this;
        }

        public ILoadInstruction GetDefaultInstruction(Type target)
        {
            var instructionType = typeof(LoadInstruction<>).MakeGenericType(target);
            return (ILoadInstruction)Activator.CreateInstance(instructionType);
        }

        public ILoadInstruction GetInstructionForType<C>()
        {
            return GetInstructionForType(typeof (C));
        }

        public ILoadInstruction GetInstructionForType(Type target)
        {
            if (LoadType == target)
                return this;

            return Instructions
                .Select(i => i.GetInstructionForType(target))
                .Where(i => i != null)
                .FirstOrDefault();
        }

        public static LoadInstruction<T> New()
        {
            return new LoadInstruction<T>();
        }

        public LoadInstruction()
        {
            _loadType = typeof (T);
        }
    }
}
